# -*- coding: utf-8 -*-
import asyncio
import datetime
import json
import os
import random
import urllib
import uuid
from traceback import format_exc
from urllib.parse import urlparse

import requests
from pyppeteer import launch
from pyppeteer.network_manager import Response, Request
from urllib3.exceptions import MaxRetryError, SSLError

from zc_core.middlewares.agents.user_agents import agents
from zc_core.middlewares.proxies.proxy_facade import ProxyFacade

from ctaxccgp.simple.slider_util import slide_move
from ctaxccgp.simple.token_queue import TokenQueue


data_cache = {}


class TokenCrawler(object):
    tmp_dir = 'E:/TempData/{}'.format(str(uuid.uuid4()))
    bind_count = 0
    max_bind_count = 2
    max_page_limit = 12
    item_url_tpl = 'https://ctaxccgp.zcygov.cn/items/{}'
    item_api_url_tpl = 'https://ctaxccgp.zcygov.cn/front/detail/item/{}?timestamp={}'

    def __init__(self, sku_id, *args, **kwargs):
        super(TokenCrawler, self).__init__(*args, **kwargs)
        self.sku_id = sku_id
        self.item_url = self.item_url_tpl.format(sku_id)
        self.token_queue = TokenQueue()
        self.proxy_facade = ProxyFacade()

    async def init_context(self):
        # 初始化
        if not os.path.exists(self.tmp_dir):
            os.makedirs(self.tmp_dir)
        proxy = self.proxy_facade.get_proxy()
        self.browser = await launch(
            headless=False,
            dumpio=True,
            # userDataDir=self.tmp_dir,
            devtools=False,
            args=['--proxy-server={}'.format(proxy), ],
            ignoreDefaultArgs=['--enable-automation']
        )
        self.page = await self.browser.newPage()

        # 参数设置
        await self.page.setViewport({"width": 1440, "height": 1080})
        agent = random.choice(agents)
        await self.page.setUserAgent(agent)
        await self.page.evaluate('''() => {Object.defineProperty(navigator, 'webdriver', {get: () => undefined})}''')

        # 注册拦截器
        await self.page.setRequestInterception(True)
        self.page.on("request", self.request_interceptor)
        self.page.on("response", self.response_interceptor)

    async def destroy_context(self):
        # 关闭浏览器
        await self.page.close()
        await self.browser.close()
        if not os.path.exists(self.tmp_dir):
            os.remove(self.tmp_dir)
        return

    async def run(self):
        print('------------创建------------')
        # 初始化浏览器
        await self.init_context()
        # 打开/刷新商品详情页面
        await self.reload_page()
        # 点击分页
        await self.get_next_page()
        # 关闭浏览器
        await self.destroy_context()
        print('------------销毁------------')

    # 刷新页面
    async def reload_page(self):
        try:
            # 清理记录
            self.bind_count = 0
            # data_cache = {}

            await self.page.goto(self.item_url)
            await self.page.waitFor(random.randint(500, 1200))
            await self.page.waitForSelector('div#tab-dealrecord')
            await self.page.click('div#tab-dealrecord')
            await self.page.waitFor(random.randint(1200, 1800))
        except Exception as ex:
            # 清理老环境
            await self.destroy_context()
            await asyncio.sleep(1.2)
            # 初始化浏览器
            await self.init_context()
            # 打开/刷新商品详情页面
            await self.page.goto(self.item_url)
            await asyncio.sleep(1.5)
            await self.reload_page()

    # 检查/处理滑块
    async def check_slider(self):
        slider = await self.page.querySelector('#nc_nvc_wrapper')
        if slider:
            is_hidden = await self.page.evaluate(
                '()=>{return window.getComputedStyle(document.getElementById("nc_nvc_wrapper")).display === "none";}')
            if not is_hidden:
                await slide_move(self.page)
                await self.page.waitFor(random.randint(1000, 1500))
                return

    async def get_next_page(self):
        try:
            await self.check_slider()

            for cnt in range(1, self.max_page_limit):
                slider = await self.page.querySelector('#nc_nvc_wrapper')
                if slider:
                    await self.page.evaluate('()=>{getNC();}')
                    await asyncio.sleep(random.randint(300, 500) / 1000)

                    await slide_move(self.page)
                    await self.page.waitFor(random.randint(1000, 1500))
                    val = await self.page.evaluate('()=>{return getNVCVal();}')
                    print('{}[{}]、====> {}'.format(cnt, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), val))
                    self.token_queue.push(val)
                    self.bind_count = self.bind_count + 1
                    # 刷新
                    if self.bind_count > self.max_bind_count:
                        await self.reload_page()
        except Exception:
            await self.page.waitFor(random.randint(500, 1500))
            await self.reload_page()

    async def response_interceptor(self, response: Response):
        if "front/detail/item/dealRecord" in response.url:
            content = await response.text()
            json_data = json.loads(content)
            if json_data and not json_data.get('success'):
                rs_code = json_data.get('code', '')
                if rs_code == '403':
                    # 403直接刷新
                    return await self.reload_page()
                elif rs_code == '400':
                    # 400观察统计，超过阈值刷新
                    self.bind_count = self.bind_count + 1
                    if self.bind_count > self.max_bind_count:
                        return await self.reload_page()

    async def request_interceptor(self, request: Request):
        if request.resourceType in ['image', 'websocket', 'other']:
            return await request.abort()

        if 'front/detail/item/dealRecord' in request.url:
            return await self.send_cache_request(request)

        if 'cf.aliyun.com/nocaptcha' in request.url \
                or 'service/um.json' in request.url \
                or 'front/detail/item' in request.url:
            return await self.send_no_cache_request(request)

        if request.resourceType in ['document']:
            return await self.send_no_cache_request(request)

        if request.resourceType in ['xhr']:
            return await self.send_cache_request(request)

        return await request.continue_()

    async def send_cache_request(self, request):
        path = urlparse(request.url).path
        if path in data_cache:
            cache = data_cache.get(path)
            resp = {"body": cache.content, "headers": cache.headers, "status": cache.status_code}
            return await request.respond(resp)

        proxy = self.proxy_facade.get_proxy()
        try:
            response = requests.request(
                url=request.url,
                method=request.method,
                headers=request.headers,
                data=request.postData,
                timeout=180,
                proxies={
                    "https": "http://{}".format(proxy),
                },
            )
            # json_data = json.loads(response.text)
            # if json_data and json_data.get('success'):
            #     data_cache[path] = response
            data_cache[path] = response
            resp = {"body": response.content, "headers": response.headers, "status": response.status_code}
            return await request.respond(resp)
        except Exception as e:
            if isinstance(e, (
                    MaxRetryError, requests.exceptions.ProxyError, SSLError, TimeoutError, ConnectionRefusedError,
                    IOError)):
                self.proxy_facade.mark_fail(proxy)
                await asyncio.sleep(5)
            else:
                print(format_exc())
            return await self.send_cache_request(request)

    async def send_no_cache_request(self, request):
        proxy = self.proxy_facade.get_proxy()
        try:
            response = requests.request(
                url=request.url,
                method=request.method,
                headers=request.headers,
                data=request.postData,
                timeout=180,
                proxies={
                    "https": "http://{}".format(proxy),
                },
            )
            resp = {"body": response.content, "headers": response.headers, "status": response.status_code}
            return await request.respond(resp)
        except Exception as e:
            if isinstance(e, (
                    MaxRetryError, requests.exceptions.ProxyError, SSLError, TimeoutError, ConnectionRefusedError,
                    IOError)):
                self.proxy_facade.mark_fail(proxy)
                await asyncio.sleep(5)
                pass
            else:
                print(format_exc())
            return await self.send_no_cache_request(request)


if __name__ == '__main__':
    while True:
        asyncio.get_event_loop().run_until_complete(TokenCrawler('18636828').run())
